package com.jellyfishumbrella;

import static org.lwjgl.opengl.GL11.GL_LINES;
import static org.lwjgl.opengl.GL11.GL_LINE_LOOP;
import static org.lwjgl.opengl.GL11.glColor4f;

import java.util.ArrayList;
import java.util.Random;

import com.jellyfishumbrella.GL.AABB;
import com.jellyfishumbrella.GL.MeshGL;
import com.jellyfishumbrella.GL.Normal;
import com.jellyfishumbrella.GL.Plane;
import com.jellyfishumbrella.GL.Polygon;
import com.jellyfishumbrella.GL.PolygonGroup;
import com.jellyfishumbrella.GL.Vector3d;
import com.jellyfishumbrella.GL.Vertex;
import com.jellyfishumbrella.noise.FMB_RMF;
import com.jellyfishumbrella.noise.PerlinNoise;
import com.jellyfishumbrella.noise.SimplexNoise;

public class VorLand3D extends MeshGL {
	public int		numCells;
	public float	gridSpacing;
	public float	bounds		= 500;
	float			ratio;
	public float	scaling		= 5;
	public float	noisiness	= 1.0f;
	FMB_RMF			noise		= new FMB_RMF(0);
	PerlinNoise		pNoise		= new PerlinNoise();

	float			min			= 99999;
	float			max			= -99999;

	public AABB		aabb;

	PolygonGroup	mainGroup	= new PolygonGroup();

	public VorLand3D(int numCells) {
		glMode = GL_LINE_LOOP;

		glColor4f(1.0f, 1.0f, 1.0f, 0.25f);

		this.numCells = numCells;
		this.gridSpacing = 2 * bounds / numCells;
		ratio = gridSpacing / bounds;
		makeAABB();
	}

	public void displayCloud() {
		for (int i = -numCells / 2; i < numCells / 2; i++) {
			for (int j = -numCells / 2; j < numCells / 2; j++) {
				for (int k = -numCells / 2; k < numCells / 2; k++) {
					Vertex p = new Vertex(getCloudPoint(i, j, k));
					this.addSinglePointPolygon(mainGroup, p);
				}
			}
		}
		finalize();
	}

	public void displayGridCells() {
		// make cubes, and connect the corners to the cloud points inside them
		// to check to make sure we're not going out of the cell
		glMode = GL_LINES;
		for (int i = -numCells / 2; i < (numCells) / 2; i++) {
			for (int j = -numCells / 2; j < (numCells) / 2; j++) {
				for (int k = -numCells / 2; k < (numCells) / 2; k++) {
					Vertex p = new Vertex(getCloudPoint(i, j, k));
					this.addSinglePointPolygon(mainGroup, p);

					Vertex p1 = new Vertex();
					Vertex p2 = new Vertex();
					Vertex p3 = new Vertex();
					Vertex p4 = new Vertex();
					Vertex p5 = new Vertex();
					Vertex p6 = new Vertex();
					Vertex p7 = new Vertex();
					Vertex p8 = new Vertex();
					p1.x = (float) (i * gridSpacing);
					p1.y = (float) (j * gridSpacing);
					p1.z = (float) (k * gridSpacing);
					p2.add(p1, new Vertex(gridSpacing, 0, 0));
					p3.add(p1, new Vertex(0, gridSpacing, 0));
					p4.add(p1, new Vertex(0, 0, gridSpacing));
					p5.add(p1, new Vertex(gridSpacing, gridSpacing, 0));
					p6.add(p1, new Vertex(0, gridSpacing, gridSpacing));
					p7.add(p1, new Vertex(gridSpacing, 0, gridSpacing));
					p8.add(p1, new Vertex(gridSpacing, gridSpacing, gridSpacing));
					this.addLine(mainGroup, p1, p);
					this.addLine(mainGroup, p2, p);
					this.addLine(mainGroup, p3, p);
					this.addLine(mainGroup, p4, p);
					this.addLine(mainGroup, p5, p);
					this.addLine(mainGroup, p6, p);
					this.addLine(mainGroup, p7, p);
					this.addLine(mainGroup, p8, p);
					this.addLine(mainGroup, p1, p2);
					this.addLine(mainGroup, p1, p3);
					this.addLine(mainGroup, p1, p4);
					this.addLine(mainGroup, p4, p7);
					this.addLine(mainGroup, p3, p5);
					this.addLine(mainGroup, p6, p8);
					this.addLine(mainGroup, p7, p8);
					this.addLine(mainGroup, p5, p8);
					this.addLine(mainGroup, p4, p6);
					this.addLine(mainGroup, p3, p6);
					this.addLine(mainGroup, p2, p7);
					this.addLine(mainGroup, p2, p5);
				}
			}
		}
		finalize();
	}

	public Vector3d getCloudPoint(int i, int j, int k) {
		float nScale = 0.1f;
		Random R = new Random();

		Vector3d p1 = new Vector3d();
		Vector3d p2 = new Vector3d();
		Vector3d p3 = new Vector3d();
		Vector3d p4 = new Vector3d();
		p1.x = (float) ((i * gridSpacing) + gridSpacing / 2.0);
		p1.y = (float) ((j * gridSpacing) + gridSpacing / 2.0);
		p1.z = (float) ((k * gridSpacing) + gridSpacing / 2.0);

		float nx = (float) (-0.25f + 0.5f * noisiness * SimplexNoise.noise(nScale * p1.x, nScale * p1.y, nScale * p1.z));
		float ny = (float) (-0.25f + 0.5f * noisiness * SimplexNoise.noise(nScale * p1.x, nScale * p1.y, nScale * p1.z));
		float nz = (float) (-0.25f + 0.5f * noisiness * SimplexNoise.noise(nScale * p1.x, nScale * p1.y, nScale * p1.z));

		min = Math.min(min, nx);
		max = Math.max(max, nx);

		p2.x = p1.x + (gridSpacing / 2 * nx);
		p2.y = p1.y + (gridSpacing / 2 * ny);
		p2.z = p1.z + (gridSpacing / 2 * nz);

		p4.x = Math.min(p1.x + (gridSpacing / 2), Math.max(p1.x - (gridSpacing / 2), p2.x));
		p4.y = Math.min(p1.y + (gridSpacing / 2), Math.max(p1.y - (gridSpacing / 2), p2.y));
		p4.z = Math.min(p1.z + (gridSpacing / 2), Math.max(p1.z - (gridSpacing / 2), p2.z));

		// System.out.println(nx + " " + ny + " " + nz + " " + p4.x + " " + p4.y
		// + " " + p4.z + " " + min + " " + max);
		return p4;
	}

	public void makeAABB() {
		aabb = new AABB(this);
		Vertex A = new Vertex(-bounds, bounds, -bounds);
		Vertex B = new Vertex(-bounds, bounds, bounds);
		Vertex C = new Vertex(-bounds, -bounds, bounds);
		Vertex D = new Vertex(-bounds, -bounds, -bounds);

		Vertex E = new Vertex(bounds, bounds, -bounds);
		Vertex F = new Vertex(bounds, bounds, bounds);
		Vertex G = new Vertex(bounds, -bounds, bounds);
		Vertex H = new Vertex(bounds, -bounds, -bounds);

		aabb.left.verts.add(A);
		aabb.left.verts.add(B);
		aabb.left.verts.add(C);
		aabb.left.verts.add(D);

		aabb.right.verts.add(F);
		aabb.right.verts.add(E);
		aabb.right.verts.add(H);
		aabb.right.verts.add(G);

		aabb.top.verts.add(A);
		aabb.top.verts.add(B);
		aabb.top.verts.add(F);
		aabb.top.verts.add(E);

		aabb.bottom.verts.add(D);
		aabb.bottom.verts.add(C);
		aabb.bottom.verts.add(G);
		aabb.bottom.verts.add(H);

		aabb.front.verts.add(A);
		aabb.front.verts.add(E);
		aabb.front.verts.add(H);
		aabb.front.verts.add(D);

		aabb.back.verts.add(F);
		aabb.back.verts.add(B);
		aabb.back.verts.add(C);
		aabb.back.verts.add(G);

		mainGroup.polygons.add(aabb.left);
		mainGroup.polygons.add(aabb.right);
		mainGroup.polygons.add(aabb.top);
		mainGroup.polygons.add(aabb.bottom);
		mainGroup.polygons.add(aabb.front);
		mainGroup.polygons.add(aabb.back);
	}

	public void displayAABB() {
		addPolygon(mainGroup, aabb.left, false);
		addPolygon(mainGroup, aabb.right, false);
		addPolygon(mainGroup, aabb.top, false);
		addPolygon(mainGroup, aabb.bottom, false);
		addPolygon(mainGroup, aabb.front, false);
		addPolygon(mainGroup, aabb.back, false);

		finalize();
	}

	public void makeVoronoi() {
		for (int i = (-numCells / 2) + 1; i <= ((numCells) / 2) - 1; i++) {
			for (int j = (-numCells / 2) + 1; j <= ((numCells) / 2) - 1; j++) {
				for (int k = (-numCells / 2) + 1; k <= ((numCells) / 2) - 1; k++) {
					addVoronoiPoint(i, j, k);
				}
			}
		}
		synchronized (polyGroups) {
			for (PolygonGroup currPolyGroup : polyGroups) {
				for (Polygon currPoly : currPolyGroup.polygons) {
					this.addPolygon(currPolyGroup, currPoly, false);
				}
			}
		}
		finalize();
	}

	public void addVoronoiPoint(int i, int j, int k) {
		Vector3d point = getCloudPoint(i, j, k);
		Vector3d minPoint = new Vector3d();
		double minDist = 999999;

		for (int i2 = i - 1; i2 <= i + 1; i2++) {
			for (int j2 = j - 1; j2 <= j + 1; j2++) {
				for (int k2 = k - 1; k2 <= k + 1; k2++) {
					if (!(i == i2 && j == j2 && k == k2)) {
						Vector3d p2 = getCloudPoint(i2, j2, k2);
						double d = point.distance(p2);
						if (d < minDist) {
							minPoint = p2;
							minDist = d;
						}
					}
				}
			}
		}
		Vector3d midPoint = new Vector3d((point.x + minPoint.x) / 2, (point.y + minPoint.y) / 2, (point.z + minPoint.z) / 2);
		System.out.println(i + " " + j + " " + k + " " + midPoint);

		Normal planeNormal = new Normal();
		planeNormal.sub(midPoint, point);

		Plane plane = new Plane(midPoint, planeNormal);

		ArrayList<Polygon> newPolys = new ArrayList<Polygon>();
		ArrayList<Polygon> deletePolys = new ArrayList<Polygon>();
		Polygon planeAsPolygon = new Polygon(this);

/*
 * need to convert this to use the polygon groups, but I don't feel like it right now...
 * for (Polygon currPoly : polygons) {
 * if (currPoly.verts.size() == 0) {
 * deletePolys.add(currPoly);
 * } else {
 * Polygon[] splitResult = currPoly.splitByPlane(plane);
 * if (splitResult.length > 0) {
 * newPolys.add(splitResult[0]);
 * newPolys.add(splitResult[1]);
 * deletePolys.add(currPoly);
 * 
 * if (splitResult[2].verts.size() > 0) {
 * planeAsPolygon.verts.add(splitResult[2].verts.get(0));
 * if (splitResult[2].verts.size() > 1) {
 * planeAsPolygon.verts.add(splitResult[2].verts.get(1));
 * }
 * }
 * }
 * }
 * }
 * if (planeAsPolygon.verts.size() > 0) {
 * polygons.add(planeAsPolygon);
 * }
 */
		/*
		 * for (Polygon newPoly : newPolys) {
		 * polygons.add(newPoly);
		 * }
		 * for (Polygon deletePoly : deletePolys) {
		 * mainGroup.polygons.remove(deletePoly);
		 * }
		 */
	}
}
